#ifdef WIN32
#include <Winsock2.h>
#endif
#include "Socket.h"
#include <assert.h>
#include "OSThread.h"
#include "log_api.h"


Socket::Socket(UInt32 inSocketType):m_s32FileDesc(-1), m_u32State(inSocketType)
{
    fLocalAddr.sin_addr.s_addr = 0;
    fLocalAddr.sin_port = 0;

    fDestAddr.sin_addr.s_addr = 0;
    fDestAddr.sin_port = 0;
}


Socket::~Socket()
{
}

OS_Error Socket::Open(int theType)
{
    m_s32FileDesc = ::socket(PF_INET, theType, 0);
    if (m_s32FileDesc == kInvalidFileDesc)
        return OS_CannotOpenSocket;
    if(kNonBlockingSocketType & m_u32State)
        InitNonBlocking(m_s32FileDesc);
    return OS_NoErr;
}

void Socket::InitNonBlocking(int inFileDesc)
{
#ifdef __Win32__
    u_long one = 1;
    int err = ::ioctlsocket(inFileDesc, FIONBIO, &one);
#else
    int flag = ::fcntl(inFileDesc, F_GETFL, 0);
    int err = ::fcntl(inFileDesc, F_SETFL, flag | O_NONBLOCK);
#endif
    assert(err == 0);
}

void Socket::ReuseAddr()
{
    int one = 1;
    int err = ::setsockopt(m_s32FileDesc, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(int));
    assert(err == 0);
}

void Socket::Cleanup()
{
    int err = 0;

    if (m_s32FileDesc != kInvalidFileDesc)
    {
#ifdef WIN32
            err = closesocket(m_s32FileDesc);
#else
            err = close(m_s32FileDesc);
#endif
    }
    assert(err == 0);

    //m_u32State ^= kConnected;
    //m_u32State ^= kBound;
    m_u32State &= (~kBound);
    m_u32State &= (~kConnected);

    m_s32FileDesc = kInvalidFileDesc;
}

void Socket::NoDelay()
{
    int one = 1;
    int err = ::setsockopt(m_s32FileDesc, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof(int));
    assert(err == 0);
}
void Socket::KeepAlive()
{
    int one = 1;
    int err = ::setsockopt(m_s32FileDesc, SOL_SOCKET, SO_KEEPALIVE, (char*)&one, sizeof(int));
    assert(err == 0);
}

void    Socket::SetSocketBufSize(UInt32 inNewSize)
{
    int bufSize = inNewSize;
    int err = ::setsockopt(m_s32FileDesc, SOL_SOCKET, SO_SNDBUF, (char*)&bufSize, sizeof(int));
    //AssertV(err == 0, OSThread::GetErrno());
}

OS_Error    Socket::SetSocketRcvBufSize(UInt32 inNewSize)
{
    int bufSize = inNewSize;
    int err = ::setsockopt(m_s32FileDesc, SOL_SOCKET, SO_RCVBUF, (char*)&bufSize, sizeof(int));

    if (err == -1)
        return OSThread::GetErrno();

    return OS_NoErr;
}

//OS_Error    Socket::Bind(const char *addr, UInt16 port)
//{
//    int len = sizeof(fLocalAddr);
//    ::memset(&fLocalAddr, 0, sizeof(fLocalAddr));
//    fLocalAddr.sin_family = AF_INET;
//    fLocalAddr.sin_port = htons(port);
//    fLocalAddr.sin_addr.s_addr = inet_addr(addr);
//
//    int err = ::bind(m_s32FileDesc, (sockaddr *)&fLocalAddr, sizeof(fLocalAddr));
//
//    if (err == -1)
//    {
//        fLocalAddr.sin_port = 0;
//        fLocalAddr.sin_addr.s_addr = 0;
//        return (OS_Error)OSThread::GetErrno();
//    }
//    else ::getsockname(m_s32FileDesc, (sockaddr *)&fLocalAddr, &len); // get the kernel to fill in unspecified values
//    m_u32State |= kBound;
//    return OS_NoErr;
//}

OS_Error Socket::Bind(UInt32 addr, UInt16 port)
{
    int len = sizeof(fLocalAddr);
    ::memset(&fLocalAddr, 0, sizeof(fLocalAddr));
    fLocalAddr.sin_family = AF_INET;
    fLocalAddr.sin_port = htons(port);
    fLocalAddr.sin_addr.s_addr = htonl(addr);

    int err = ::bind(m_s32FileDesc, (sockaddr *)&fLocalAddr, sizeof(fLocalAddr));

    if (err == -1)
    {
        fLocalAddr.sin_port = 0;
        fLocalAddr.sin_addr.s_addr = 0;
        return (OS_Error)OSThread::GetErrno();
    }
    else ::getsockname(m_s32FileDesc, (sockaddr *)&fLocalAddr, &len); // get the kernel to fill in unspecified values
    m_u32State |= kBound;
    return OS_NoErr;
}

OS_Error Socket::Send(const char* inData, const UInt32 inLength, UInt32* outLengthSent)
{
    assert(inData != NULL);

    if (!(m_u32State & kConnected))
        return (OS_Error)ENOTCONN;

    int err;
    do {
        err = ::send(m_s32FileDesc, inData, inLength, 0);//flags??
    } while ((err == -1) && (OSThread::GetErrno() == EINTR));
    if (err == -1)
    {
        //Are there any errors that can happen if the client is connected?
        //Yes... EAGAIN. Means the socket is now flow-controleld
        int theErr = OSThread::GetErrno();
        if ((theErr != EAGAIN) && (this->IsConnected()))
        {
            //m_u32State ^= kConnected;//turn off connected state flag

            m_u32State &= (~kConnected);
			LOG_OUTPUT("Err:theErr=%d,m_u32State=%d\r\n", theErr, m_u32State);
        }
        return (OS_Error)theErr;
    }

    *outLengthSent = err;
    return OS_NoErr;
}

OS_Error Socket::Read(void *buffer, const UInt32 length, UInt32 *outRecvLenP)
{
    assert(outRecvLenP != NULL);
    assert(buffer != NULL);

    if (!(m_u32State & kConnected))
        return (OS_Error)ENOTCONN;

    //int theRecvLen = ::recv(m_s32FileDesc, buffer, length, 0);//flags??
    int theRecvLen;
    do {
        theRecvLen = ::recv(m_s32FileDesc, (char*)buffer, length, 0);//flags??
    } while ((theRecvLen == -1) && (OSThread::GetErrno() == EINTR));

    if (theRecvLen == -1)
    {
        // Are there any errors that can happen if the client is connected?
        // Yes... EAGAIN. Means the socket is now flow-controleld
        int theErr = OSThread::GetErrno();
        if ((theErr != EAGAIN) && (this->IsConnected()))
        {

            //m_u32State ^= kConnected;//turn off connected state flag
            m_u32State &= (~kConnected);//turn off connected state flag
			LOG_OUTPUT("Err:recv theErr=%d\r\n", theErr);
        }
        return (OS_Error)theErr;
    }
    //if we get 0 bytes back from read, that means the client has disconnected.
    //Note that and return the proper error to the caller
    else if (theRecvLen == 0)
    {
        //m_u32State ^= kConnected;

		//modify by carl
        m_u32State &= (~kConnected);//turn off connected state flag
		LOG_OUTPUT("Warn:recv len=0,errno=%d\r\n", OSThread::GetErrno());
        return (OS_Error)ENOTCONN;
    }
    assert(theRecvLen > 0);
    *outRecvLenP = (UInt32)theRecvLen;
    return OS_NoErr;
}

OS_Error Socket::WriteV(const struct iovec* iov, const UInt32 numIOvecs, UInt32* outLenSent)
{
    assert(iov != NULL);

    if (!(m_u32State & kConnected))
        return (OS_Error)ENOTCONN;

    int err;
    do {
#ifdef __Win32__
        DWORD theBytesSent = 0;
        err = ::WSASend(m_s32FileDesc, (LPWSABUF)iov, numIOvecs, &theBytesSent, 0, NULL, NULL);
        if (err == 0)
            err = theBytesSent;
#else
        err = ::writev(m_s32FileDesc, iov, numIOvecs);//flags??
#endif
    } while ((err == -1) && (OSThread::GetErrno() == EINTR));
    if (err == -1)
    {
        // Are there any errors that can happen if the client is connected?
        // Yes... EAGAIN. Means the socket is now flow-controleld
        int theErr = OSThread::GetErrno();
        if ((theErr != EAGAIN) && (this->IsConnected()))
        {
            //m_u32State ^= kConnected;//turn off connected state flag
            m_u32State &= (~kConnected);//turn off connected state flag
        }
        return (OS_Error)theErr;
    }
    if (outLenSent != NULL)
        *outLenSent = (UInt32)err;

    return OS_NoErr;
}